/* 
 * Copyright (c) 2010 Adam Markiewicz
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file meta/traits.h
 * @author Adam Markiewicz
 * 
 * @brief Support for type traits
 */

#ifndef __CLEA_META_TRAITS
#define __CLEA_META_TRAITS

#include <config.h>
#include <core/types.h>

/**
 * @defgroup type_traits Type traits
 * 
 * @ingroup meta
 * 
 * This module provides extensions to template meta-programming which allow to
 * determine traits of a type at compile-time and thus make advanced
 * optimizations possible. The avilability of specific traits depends on the
 * compiler used. You need to check trait documentation to check whether your 
 * compiler is supported.
 * 
 * @section usage Usage
 * Example:
 * @code
 * #include <meta/traits.h>
 * 
 * using namespace Clea::Meta;
 * 
 * // Default implementation
 * template <typename T, bool IsPod = Traits<T>::isPOD>
 * void sort(int count, T* data)
 * {
 *     ...
 * }
 * 
 * // Implementation optimized for plain-old-data
 * template <typename T>
 * void sort<T, true>(int count, T* data)
 * {
 *     ...
 * }
 * @endcode
 * 
 * Alternatively:
 * @code
 * #include <meta/traits.h>
 * 
 * // Default implementation
 * template <typename T, bool IsPod = IsPOD<T>::value>
 * void sort(int count, T* data)
 * {
 *     ...
 * }
 * 
 * // Implementation optimized for plain-old-data
 * template <typename T>
 * void sort<T, true>(int count, T* data)
 * {
 *     ...
 * }
 * @endcode
 * Note that the Clea::Meta::IsPOD struct is declared in the global namespace.
 * this is done to allow easy specialization for user-defined types. Every trait
 * has its global equivalent.
 * 
 * @section user_types User-defined types
 * 
 * You can override traits of user-defined types by specialization:
 * @code
 * #incldue <meta/traits.h>
 * 
 * template <>
 * struct IsPOD<MyType>
 * {
 *     enum { value = 1 };
 * };
 * @endcode
 * Note that type traits extensions in compilers are used if avilable so 
 * normally this is not neccessary.
 */

#ifdef CLEA_NO_GLOBAL_TRAITS
namespace Clea
{
namespace Meta
{
#endif

/**
 * @brief True if the assignment operator doesn't throw exceptions
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasNoThrowAssign
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_nothrow_assign(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if the copy constructor doesn't throw exceptions
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasNoThrowCopy
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_nothrow_copy(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if constructor doesn't throw exceptions
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasNoThrowConstructor
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_nothrow_constructor(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if default assignment operator is used
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasTrivialAssign
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_trivial_assign(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True is default copy constructor is used
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasTrivialCopy
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_trivial_copy(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if default constructor is used
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasTrivialConstructor
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_trivial_constructor(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if default destructor is used
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasTrivialDestructor
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_trivial_destructor(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if destructor is virtual
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasVirtualDestructor
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __has_virtual_destructor(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if type is abstract
 * 
 * @section default Default
 * false
 * 
 * @section Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsAbstract
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_abstract(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if type is a class
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsClass
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_class(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if type is empty
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsEmpty
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_empty(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True is type is an enumeration
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsEnum
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_enum(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True is type is plain-old-data
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsPOD
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_pod(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if type is polymorphic
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsPolymorphic
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_polymorphic(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True is type is a union
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 1. GCC
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsUnion
{
    #ifdef CLEA_COMPILER_GCC
    enum { value = __is_union(T) };
    #else
    enum { value = 0 };
    #endif
};

/**
 * @brief True if default comparison operator is used
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct HasTrivialCompare
{
    enum { value = 0 };
};

/**
 * @brief True if type is an integer
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * All compilers
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsInteger
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::int8>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::int16>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::int32>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::int64>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::uint8>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::uint16>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::uint32>
{
    enum { value = 0 };
};

template <>
struct IsInteger<Clea::Core::uint64>
{
    enum { value = 0 };
};

/**
 * @brief True if type is an unsigned integer
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * All compilers
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsUnsigenedInteger
{
    enum { value = 0 };
};

template <>
struct IsUnsigenedInteger<Clea::Core::uint8>
{
    enum { value = 1 };
};

template <>
struct IsUnsigenedInteger<Clea::Core::uint16>
{
    enum { value = 1 };
};

template <>
struct IsUnsigenedInteger<Clea::Core::uint32>
{
    enum { value = 1 };
};

template <>
struct IsUnsigenedInteger<Clea::Core::uint64>
{
    enum { value = 1 };
};

/**
 * @brief True if type is a pointer
 * 
 * @section default Default
 * false
 * 
 * @section supported Supported in:
 * All compilers
 * 
 * @ingroup type_traits
 * @see Clea::Meta::Traits
 * 
 * @tparam T Type
 */
template <typename T>
struct IsPointer
{
    enum { value = 0 };
};

template <typename T>
struct IsPointer<T*>
{
    enum { value = 1 };
};

#ifdef CLEA_NO_GLOBAL_TRAITS
}
}
#endif





namespace Clea
{
    namespace Meta
    {
        /**
         * @brief Type traits support
         * 
         * @ingroup type_traits
         * 
         * @tparam Type Type
         */
        template <typename Type>
        class Traits
        {
            public:
                typedef Type value_type;
                typedef Type* pointer;
                typedef const Type* const_pointer;
                typedef Type& reference;
                typedef const Type& const_reference;
                
            #ifdef CLEA_NO_GLOBAL_TRAITS
            private:
                using Clea::Meta::HasNoThrowAssign;
                using Clea::Meta::HasNoThrowCopy;
                using Clea::Meta::HasNoThrowConstructor;
                using Clea::Meta::HasTrivialAssign;
                using Clea::Meta::HasTrivialCopy;
                using Clea::Meta::HasTrivialConstructor;
                using Clea::Meta::HasTrivialDestructor;
                using Clea::Meta::HasVirtualDestructor;
                using Clea::Meta::IsAbstract;
                using Clea::Meta::IsClass;
                using Clea::Meta::IsEmpty;
                using Clea::Meta::IsEnum;
                using Clea::Meta::IsPOD;
                using Clea::Meta::IsPolymorphic;
                using Clea::Meta::IsUnion;
                using Clea::Meta::HasTrivialCompare;
                using Clea::Meta::IsInteger;
                using Clea::Meta::IsUnsigenedInteger;
                using Clea::Meta::IsPointer;
            #endif // CLEA_NO_GLOBAL_TRAITS
            
            public:
                enum {
                    hasNoThrowAssign = HasNoThrowAssign<value_type>::value,
                    hasNoThrowCopy = HasNoThrowCopy<value_type>::value,
                    hasNoThrowConstructor = HasNoThrowConstructor<value_type>::value,
                    hasTrivialAssign = HasTrivialAssign<value_type>::value,
                    hasTrivialCopy = HasTrivialCopy<value_type>::value,
                    hasTrivialConstructor = HasTrivialConstructor<value_type>::value,
                    hasTrivialDestructor = HasTrivialDestructor<value_type>::value,
                    hasVirtualDestructor = HasVirtualDestructor<value_type>::value,
                    isAbstract = IsAbstract<value_type>::value,
                    isClass = IsClass<value_type>::value,
                    isEmpty = IsEmpty<value_type>::value,
                    isEnum = IsEnum<value_type>::value,
                    isPOD = IsPOD<value_type>::value,
                    isPolymorphic = IsPolymorphic<value_type>::value,
                    isUnion = IsAbstract<value_type>::value,
                    hasTrivialCompare = HasTrivialCompare<value_type>::value,
                    isInteger = IsInteger<value_type>::value,
                    isUnsignedInteger = IsUnsigenedInteger<value_type>::value,
                    isPointer = IsPointer<value_type>::value
                };
        };
    }
}

#endif // __CLEA_META_TRAITS
